#pragma once

#include <map>
#include <string>

using namespace std;

#include "pugixml/pugixml.hpp"
#include "topcvt_cross_platform.hpp"

namespace topcvt
{
    const static string ROOTCFG_FORMAT_STR = "str";
    const static string ROOTCFG_FORMAT_HEX = "hex";
    const static string ROOTCFG_FORMAT_MOCK = "mock";

    enum class cfgtype_t { 
        E_ROOT,
        E_INCLUDE,
        E_POLICY,
        E_STRUCT,
        E_FIELD,
        E_LOOP,
        E_SWITCH,
        E_CASE
    };

    class AttributeableCfg 
    {
    protected:
        map<string, string> attributes;
        bool validated;
        cfgtype_t cfgtype;
        string name;
    public:
        AttributeableCfg() : validated(true) {};
        AttributeableCfg(cfgtype_t type) : validated(true), cfgtype(type){};
        virtual ~AttributeableCfg(){};
        virtual inline string& getName() { return name; };

        inline map<string, string>& getAttributes() {return attributes;}
        inline void set(const string& key, const string& value)  { attributes[key] = value; }

        inline string get(const string& key) 
        {
            string value("");
            auto iter = attributes.find(key);
            if(iter != attributes.end())
                value = iter->second;
            return value;
        }

        inline bool isValidated() { return this->validated; }
        inline void setValidated(bool attrValid) { validated = attrValid; }
        inline cfgtype_t getCfgType() { return cfgtype; }
    };

    class RootCfg : public AttributeableCfg
    {
    private:
        string consoleFormat;
        string consoleEncoding;
        pugi::xml_document document;
        pugi::xml_node node; 
    public:
        RootCfg() : AttributeableCfg(cfgtype_t::E_ROOT) {};
        inline void setFormat(const string& format) { this->consoleFormat = format; };
        inline void setEncoding(const string& encoding) { this->consoleEncoding = encoding; };     
        inline pugi::xml_document& getDocument() { return this->document;};   
        inline string& getFormat() { return this->consoleFormat;};
        inline string& getEncoding() { return this->consoleEncoding;};
        inline pugi::xml_node getRoot(){ return this->document.child("root"); };
    };

    class IncludeCfg : public AttributeableCfg
    {
    private:
        string file;
        string desc;
    public:
        IncludeCfg() : AttributeableCfg(cfgtype_t::E_INCLUDE) {};
        inline void setFile(const string& file){ this->file = file; };
        inline void setDesc(const string& desc){ this->desc = desc; };
        inline string& getFile() { return this->file;};
        inline string& getDesc() { return this->desc;};
    };

    class PolicyCfg : public AttributeableCfg
    {
    private:
        string type;
        string desc;   
        pugi::xml_node node; 
    public:
        PolicyCfg() : AttributeableCfg(cfgtype_t::E_POLICY) {};
        inline void setType(const string& type){ this->type = type; };
        inline void setDesc(const string& desc){ this->desc = desc; };
        inline string& getType() { return this->type;};
        inline string& getDesc() { return this->desc;};  

        inline void setElement(pugi::xml_node& inode) { this->node = inode;};
        inline pugi::xml_node& getElement(){ return node;};
    };

    class StructCfg : public AttributeableCfg
    {
    private:
        string name;
        string desc;
        pugi::xml_node node; 
    public:
        StructCfg() : AttributeableCfg(cfgtype_t::E_STRUCT) {};
        inline void setName(const string& name){ this->name = name; };
        inline void setDesc(const string& desc){ this->desc = desc; };
        inline string& getName() { return this->name;};
        inline string& getDesc() { return this->desc;};  
        inline void setElement(pugi::xml_node& inode) { this->node = inode;};
        inline pugi::xml_node& getElement(){ return node;};        
    };  

    class LoopCfg : public AttributeableCfg
    {
    private:
        string name;
        string desc;
        string opcount;
        string len;
        pugi::xml_node node; 
    public:
        LoopCfg() : AttributeableCfg(cfgtype_t::E_LOOP) {};
        inline void setName(const string& name){ this->name = name; };
        inline void setDesc(const string& desc){ this->desc = desc; };
        inline void setOpcount(const string& opcount){ this->opcount = opcount; };
        inline void setLen(const string& len) { this->len = len;};
        inline string& getName() { return this->name;};
        inline string& getDesc() { return this->desc;};  
        inline string& getOpcount() { return this->opcount;};  
        inline string& getLen() { return this->len;};  
        inline void setElement(pugi::xml_node& inode) { this->node = inode;};
        inline pugi::xml_node& getElement(){ return node;};        
    };

    class FieldCfg : public AttributeableCfg
    {
    private:
        string name;
        string desc;
        string value;
        string defval;
        string cType;
        string pktType;
        PolicyCfg policyCfg;
        string trimOption;   
    public:
        FieldCfg() : AttributeableCfg(cfgtype_t::E_FIELD) {};
        inline void setName(const string& name){ this->name = name; };
        inline void setDesc(const string& desc){ this->desc = desc; };
        inline void setValue(const string& value){ this->value = value; };
        inline void setDefval(const string& value){ this->defval = value; };
        inline void setCType(const string& cType){ this->cType = cType; };
        inline void setPktType(const string& pktType){ this->pktType = pktType; };
        inline void setPolicyCfg(const PolicyCfg& cfg){ this->policyCfg = cfg; };
        inline void setTrimOption(const string& option) { this->trimOption = option; };

        inline string& getName()   { return this->name;};
        inline string& getDesc()   { return this->desc;};  
        inline string& getValue()  { return this->value;};
        inline string& getDefval() { return this->defval;};
        inline string& getCType()  { return this->cType;};   
        inline string& getPktType(){ return this->pktType;};        
        inline PolicyCfg& getPolicyCfg(){ return this->policyCfg;};   
        inline string& getTrimOption() { return this->trimOption;};
    }; 

    class SwitchCfg : public AttributeableCfg
    {
    private:
        string desc;    
        string condition;
        pugi::xml_node node; 
    public:
        SwitchCfg() : AttributeableCfg(cfgtype_t::E_SWITCH) {};
        inline void setCondition(const string& condition){ this->condition = condition; };
        inline void setDesc(const string& desc){ this->desc = desc; };
        inline string& getCondition() { return this->condition;};
        inline string& getDesc() { return this->desc;};  
        inline void setElement(pugi::xml_node& inode) { this->node = inode;};
        inline pugi::xml_node& getElement(){ return node;};        
    };

    class CaseCfg : public AttributeableCfg
    {
    private:
        string desc;
        string value;
        pugi::xml_node node; 
    public:
        CaseCfg() : AttributeableCfg(cfgtype_t::E_CASE) {};
        inline void setDesc(const string& desc){ this->desc = desc; };
        inline void setValue(const string& value){ this->value = value; };
        inline string& getDesc()   { return this->desc;};  
        inline string& getValue()  { return this->value;};
        inline void setElement(pugi::xml_node& inode) { this->node = inode;};
        inline pugi::xml_node& getElement(){ return node;};        
    }; 
};